/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.hamacekj.geometr.internalplugins;

import cz.hamacekj.geometr.LineObject;
import cz.hamacekj.geometr.RayObject;
import cz.hamacekj.geometr.SegmentObject;
import cz.hamacekj.geometr.plugin.ConstructionObject;
import cz.hamacekj.geometr.plugin.ConstructionPlugin;
import cz.hamacekj.geometr.plugin.ILineObject;
import cz.hamacekj.geometr.plugin.ILinearObject;
import cz.hamacekj.geometr.plugin.IPoint;
import cz.hamacekj.geometr.plugin.IPointObject;
import cz.hamacekj.geometr.plugin.ISegmentObject;
import cz.hamacekj.geometr.plugin.IToolBox;
import cz.hamacekj.geometr.plugin.InputParameters;
import cz.hamacekj.geometr.plugin.InputParametersInterface;
import cz.hamacekj.geometr.plugin.ParameterItem;
import cz.hamacekj.geometr.plugin.ParameterObject;
import cz.hamacekj.geometr.plugin.ParameterString;
import cz.hamacekj.geometr.plugin.ParameterValue;
import cz.hamacekj.geometr.plugin.PrimitiveObject;
import cz.hamacekj.geometr.plugin.ValueArgument;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.swing.ImageIcon;

/**
 * Plugin pro vytvoření přímky.
 * @author hamacekh
 */
public class LinePlugin implements ConstructionPlugin {
    
    private IToolBox toolbox;
    private String error = null;

    public LinePlugin() {
    }
    
    public LinePlugin(IToolBox toolbox) {
        this.toolbox = toolbox;
    }
    
    public void setToolbox(IToolBox toolbox) {
        this.toolbox = toolbox;
    }

    @Override
    public String getPluginName() {
        return "Přímka";
    }

    @Override
    public InputParametersInterface[] getPossibleParameters() {
        InputParametersInterface[] res = {new TwoPointsParameters(),
            new PointLineParameters(), new PerpendicularPoint(),
            new HalfParameter(), new TwoLinesParameters()};
        return res;
    }

    @Override
    public ImageIcon getIcon() {
        return new ImageIcon("images/primka.png");
    }

    @Override
    public ConstructionObject[] createConstructionObject(InputParametersInterface parametersType, ConstructionObject[] objects, ValueArgument[] values) {
        ConstructionObject[] res = null;
        switch(parametersType.getParametersCaption()){
            case "Dva body":
            {
                final String realName;
                final boolean isLabelShown;
                final IPointObject A = (IPointObject)objects[0].getPrimitiveObjects().iterator().next();
                final String aLabel = objects[0].getLabel();
                final IPointObject B = (IPointObject)objects[1].getPrimitiveObjects().iterator().next();
                final String bLabel = objects[1].getLabel();
                if(values[0].getRealInput().isEmpty()){
                    realName = aLabel+bLabel;
                    isLabelShown = false;
                }else{
                    realName = values[0].getRealInput();
                    if(!realName.matches("^[a-z][0-9]*$")){
                        this.error = "Neplatný název přímky";
                        return null;
                    }
                    isLabelShown = true;
                }
                ConstructionObject line = new ConstructionObject() {

                    @Override
                    public String getLabel() {
                        return realName;
                    }

                    @Override
                    public boolean isLabelShown() {
                        return isLabelShown;
                    }

                    @Override
                    public Set<PrimitiveObject> getPrimitiveObjects() {
                        HashSet<PrimitiveObject> hs = new HashSet<>(1);
                        hs.add(toolbox.createLineObject(A.getPoint(), B.getPoint()));
                        return hs;
                    }

                    @Override
                    public String getStepDescription() {
                        if(isLabelShown){
                            return "<Přímka> "+realName+": "+aLabel+" <\\in>"+realName+" <\\and> "+bLabel+"<\\in>"+realName;
                        }else{
                            return "<Přímka> "+realName;
                        }
                    }
                };
                res = new ConstructionObject[1];
                res[0] = line;
                break;
            }
            case "Rovnoběžka procházející bodem":
            {
                final String realName = values[0].getRealInput();
                if(!realName.matches("^[a-z][0-9]*$")){
                    this.error = "Neplatný název přímky";
                    return null;
                }
                final IPoint lineA = getAPoint(objects[0]);
                final IPoint lineB = getBPoint(objects[0]);
                final String objName = objects[0].getLabel();
                final String pointName = objects[1].getLabel();
                final IPoint startPoint = ((IPointObject)objects[1]
                        .getPrimitiveObjects().iterator().next()).getPoint();
                final double vectx = lineA.getX() - lineB.getX();
                final double vecty = lineA.getY() - lineB.getY();
                ConstructionObject line1 = new ConstructionObject() {

                    @Override
                    public String getLabel() {
                        return realName;
                    }

                    @Override
                    public boolean isLabelShown() {
                        return true;
                    }

                    @Override
                    public Set<PrimitiveObject> getPrimitiveObjects() {
                        IPoint newB = toolbox.createPoint(startPoint.getX() + vectx, startPoint.getY() + vecty);
                        ILineObject obj = toolbox.createLineObject(startPoint, newB);
                        HashSet<PrimitiveObject> hs = new HashSet<>(1);
                        hs.add(obj);
                        return hs;
                    }

                    @Override
                    public String getStepDescription() {
                        return "<Přímka> "+realName+"; "+realName+" <\\parallel> "+objName+"<\\and> "+pointName+"<\\in>"+realName;
                    }
                };
                res = new ConstructionObject[1];
                res[0] = line1;
                break;
            }
            case "Kolmice procházející bodem":
            {
                final String name = values[0].getRealInput();
                if(!name.matches("^[a-z][0-9]*$")){
                    this.error = "Neplatný název přímky";
                    return null;
                }
                ConstructionObject lineObject = objects[0];
                ConstructionObject pointObject = objects[1];
                final String pointObjectName = pointObject.getLabel();
                final String lineObjectName = lineObject.getLabel();
                Collection<PrimitiveObject> lineObjects = lineObject.getPrimitiveObjects();
                ILinearObject line = (ILinearObject)lineObjects.iterator().next();
                IPointObject point = (IPointObject)pointObject.getPrimitiveObjects().iterator().next();
                IPoint A = line.getA();
                IPoint B = line.getB();
                double vectx = A.getX() - B.getX();
                double vecty = A.getY() - B.getY();
                double newVectX = vecty;
                double newVectY = -vectx;
                IPoint lastB = toolbox.createPoint(point.getPoint().getX() + newVectX,
                        point.getPoint().getY() + newVectY);
                final ILineObject resultLine = toolbox.createLineObject(point.getPoint(), lastB);
                final Set<PrimitiveObject> set = new HashSet<>();
                set.add(resultLine);
                ConstructionObject obj = new ConstructionObject() {

                    @Override
                    public String getLabel() {
                        return name;
                    }

                    @Override
                    public boolean isLabelShown() {
                        return true;
                    }

                    @Override
                    public Set<PrimitiveObject> getPrimitiveObjects() {
                        return set;
                    }

                    @Override
                    public String getStepDescription() {
                        return "<Přímka> "+name+", "+name+" <\\perp> "+lineObjectName+ " <\\and> "+pointObjectName+" <\\in> "+name;
                    }
                };
                res = new ConstructionObject[1];
                res[0] = obj;
                break;
            }
            case "Osa úsečky":
            {
                final String name = values[0].getRealInput();
                if(!name.matches("^[a-z][0-9]*$")){
                    this.error = "Neplatný název přímky";
                    return null;
                }
                ConstructionObject segmentObject = objects[0];
                final String segmentName = segmentObject.getLabel();
                final ISegmentObject segment = (ISegmentObject)segmentObject.getPrimitiveObjects().iterator().next();
                IPoint A = segment.getA();
                IPoint B = segment.getB();
                double vectx = A.getX() - B.getX();
                double vecty = A.getY() - B.getY();
                double linevx = vecty;
                double linevy = -vectx;
                IPoint center = toolbox.createPoint((A.getX()+B.getX())/2, (A.getY() + B.getY())/2);
                IPoint point = toolbox.createPoint(center.getX() + linevx, center.getY() + linevy);
                ILineObject line = toolbox.createLineObject(center, point);
                final Set<PrimitiveObject> set = new HashSet<>();
                set.add(line);
                ConstructionObject obj = new ConstructionObject() {

                    @Override
                    public String getLabel() {
                        return name;
                    }

                    @Override
                    public boolean isLabelShown() {
                        return true;
                    }

                    @Override
                    public Set<PrimitiveObject> getPrimitiveObjects() {
                        return set;
                    }

                    @Override
                    public String getStepDescription() {
                        return "<Přímka> "+name+", <Přímka> "+name+" je osou <Úsečka>"+segmentName;
                    }
                };
                res = new ConstructionObject[1];
                res[0] = obj;
                break;
            }
            case "Osa dvou přímek":
            {
                final String name = values[0].getRealInput();
                if(!name.matches("^[a-z][0-9]*$")){
                    this.error = "Neplatný název přímky";
                    return null;
                }
                ConstructionObject lineObject1 = objects[0];
                ConstructionObject lineObject2 = objects[1];
                final String line1Name = lineObject1.getLabel();
                final String line2Name = lineObject2.getLabel();
                Collection<? extends PrimitiveObject> intersection = toolbox.intersect(lineObject1, lineObject2);
                ILineObject line1 = (ILineObject)lineObject1.getPrimitiveObjects().iterator().next();
                ILineObject line2 = (ILineObject)lineObject2.getPrimitiveObjects().iterator().next();
                IPoint A1 = line1.getA();
                IPoint B1 = line1.getB();
                double vectx1 = A1.getX() - B1.getX();
                double vecty1 = A1.getY() - B1.getY();
                if(intersection.isEmpty()){
                    // vstupni primky jsou rovnobezne. Je treba najit bod mezi nima.
                    IPoint otherA = toolbox.createPoint(A1.getX() + vecty1, A1.getY() - vectx1);
                    ILineObject perpendicular = toolbox.createLineObject(A1, otherA);
                    Collection<? extends PrimitiveObject> inter = toolbox.intersect(perpendicular, line2);
                    IPoint AA2 = ((IPointObject)inter.iterator().next()).getPoint();
                    IPoint center = toolbox.createPoint((A1.getX() + AA2.getX())/2,
                            (A1.getY() + AA2.getY())/2);
                    IPoint center1 = toolbox.createPoint(center.getX() + vectx1, center.getY() + vecty1);
                    final ILineObject line = toolbox.createLineObject(center, center1);
                    final Set<PrimitiveObject> set = new HashSet<>();
                    set.add(line);
                    ConstructionObject obj = new ConstructionObject() {

                        @Override
                        public String getLabel() {
                            return name;
                        }

                        @Override
                        public boolean isLabelShown() {
                            return true;
                        }

                        @Override
                        public Set<PrimitiveObject> getPrimitiveObjects() {
                            return set;
                        }

                        @Override
                        public String getStepDescription() {
                            return "<Přímka> "+name+", <Přímka> "+name+" je osou "+"<Přímka> "+line1Name+" a <Přímka> "+line2Name;
                        }
                    };
                    res = new ConstructionObject[1];
                    res[0] = obj;
                    return res;
                }
                PrimitiveObject i = intersection.iterator().next();
                if(i instanceof ILineObject){
                    // zadane primky jsou identicke
                    this.error = "Nemá smysl vytvářet osu dvou identických přímek.";
                    return null;
                }
                if(i instanceof IPointObject){
                    // zadane primky jsou ruznobezne. hledam vektor smeru nejake osy.
                    // spocitam ho jako prumer jednotkovych vektoru zadanych primek.
                    IPoint center = ((IPointObject)i).getPoint();
                    double vectx1j = vectx1 / toolbox.distance(A1, B1);
                    double vecty1j = vecty1 / toolbox.distance(A1, B1);
                    IPoint A2 = line2.getA();
                    IPoint B2 = line2.getB();
                    double vectx2j = (A2.getX() - B2.getX()) / toolbox.distance(A2, B2);
                    double vecty2j = (A2.getY() - B2.getY()) / toolbox.distance(A2, B2);
                    double vx = (vectx1j + vectx2j) / 2;
                    double vy = (vecty1j + vecty2j) / 2;
                    IPoint first = toolbox.createPoint(center.getX() + vx, center.getY() + vy);
                    IPoint second = toolbox.createPoint(center.getX() + vy, center.getY() - vx);
                    ILineObject firstLine = toolbox.createLineObject(center, first);
                    ILineObject secondLine = toolbox.createLineObject(center, second);
                    final Set<PrimitiveObject> firstSet = new HashSet<>();
                    firstSet.add(firstLine);
                    final Set<PrimitiveObject> secondSet = new HashSet<>();
                    secondSet.add(secondLine);
                    ConstructionObject firstObject = new ConstructionObject() {

                        @Override
                        public String getLabel() {
                            return name+"1";
                        }

                        @Override
                        public boolean isLabelShown() {
                            return true;
                        }

                        @Override
                        public Set<PrimitiveObject> getPrimitiveObjects() {
                            return firstSet;
                        }

                        @Override
                        public String getStepDescription() {
                            return "<Přímka> "+getLabel()+", <Přímka> "+getLabel()+" je osou "+"<Přímka> "+line1Name+" a <Přímka> "+line2Name;
                        }
                    };
                    ConstructionObject secondObject = new ConstructionObject() {

                        @Override
                        public String getLabel() {
                            return name+"2";
                        }

                        @Override
                        public boolean isLabelShown() {
                            return true;
                        }

                        @Override
                        public Set<PrimitiveObject> getPrimitiveObjects() {
                            return secondSet;
                        }

                        @Override
                        public String getStepDescription() {
                            return "<Přímka> "+getLabel()+", <Přímka> "+getLabel()+" je osou "+"<Přímka> "+line1Name+" a <Přímka> "+line2Name;
                        }
                    };
                    res = new ConstructionObject[2];
                    res[0] = firstObject;
                    res[1] = secondObject;
                }
                assert res != null;
                break;
            }
            default:
                res = null;
        }
        return res;
    }
    
    @Override
    public String getError(){
        return error;
    }
    
    private IPoint getAPoint(ConstructionObject obj){
        PrimitiveObject prim = obj.getPrimitiveObjects().iterator().next();
        if(prim instanceof LineObject){
            return ((LineObject)prim).getA();
        }
        if(prim instanceof RayObject){
            return ((RayObject)prim).getA();
        }
        if(prim instanceof SegmentObject){
            return ((SegmentObject)prim).getA();
        }
        return null;
    }
    
    private IPoint getBPoint(ConstructionObject obj){
        PrimitiveObject prim = obj.getPrimitiveObjects().iterator().next();
        if(prim instanceof LineObject){
            return ((LineObject)prim).getB();
        }
        if(prim instanceof RayObject){
            return ((RayObject)prim).getB();
        }
        if(prim instanceof SegmentObject){
            return ((SegmentObject)prim).getB();
        }
        return null;
    }
    
    private class TwoPointsParameters extends InputParameters {

        @Override
        public String getParametersCaption() {
            return "Dva body";
        }

        @Override
        public ParameterItem[] getParameters() {
            ParameterItem[] res = new ParameterItem[5];
            res[0] = new ParameterString() {

                @Override
                public String getText() {
                    return "Jméno: ";
            }
            };
            res[1] = new ParameterValue() {

                @Override
                public String getCaption() {
                    return "Jméno";
                }
            };
            res[2] = new ParameterString() {

                @Override
                public String getText() {
                    return "Body: ";
                }
            };
            res[3] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "První bod";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = {"Bod"};
                    return res;
                }
                
            };
            res[4] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Drhuý bod";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = {"Bod"};
                    return res;
                }
            };
            return res;
        }
        
    }
    
    private class PointLineParameters extends InputParameters {

        @Override
        public String getParametersCaption() {
            return "Rovnoběžka procházející bodem";
        }

        @Override
        public ParameterItem[] getParameters() {
            ParameterItem[] res = new ParameterItem[5];
            res[0] = new ParameterString() {

                @Override
                public String getText() {
                    return "Jméno: ";
            }
            };
            res[1] = new ParameterValue() {

                @Override
                public String getCaption() {
                    return "Jméno";
                }
            };
            res[2] = new ParameterString() {

                @Override
                public String getText() {
                    return " || ";
                }
            };
            res[3] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Rovnoběžka";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Přímka", "Polopřímka", "Úsečka" };
                    return res;
                }
            };
            res[4] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Bod";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Bod" };
                    return res;
                }
            };
            return res;
        }
        
    }
    
    private class PerpendicularPoint extends InputParameters{

        @Override
        public String getParametersCaption() {
            return "Kolmice procházející bodem";
        }

        @Override
        public ParameterItem[] getParameters() {
            ParameterItem[] param = new ParameterItem[6];
            param[0] = new ParameterString() {

                @Override
                public String getText() {
                    return "Název";
                }
            };
            param[1] = new ParameterValue() {

                @Override
                public String getCaption() {
                    return "Název";
                }
            };
            param[2] = new ParameterString() {

                @Override
                public String getText() {
                    return "Kolmice";
                }
            };
            param[3] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Kolmice";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Přímka", "Polopřímka", "Úsečka" };
                    return res;
                }
            };
            param[4] = new ParameterString() {

                @Override
                public String getText() {
                    return "Prochází bodem";
                }
            };
            param[5] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Bod";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Bod" };
                    return res;
                }
            };
            return param;
        }
        
    }
    
    private class HalfParameter extends InputParameters{

        @Override
        public String getParametersCaption() {
            return "Osa úsečky";
        }

        @Override
        public ParameterItem[] getParameters() {
            ParameterItem[] res = new ParameterItem[4];
            res[0] = new ParameterString() {

                @Override
                public String getText() {
                    return "Název";
                }
            };
            res[1] = new ParameterValue() {

                @Override
                public String getCaption() {
                    return "Název";
                }
            };
            res[2] = new ParameterString() {

                @Override
                public String getText() {
                    return "Úsečka ke které hledám osu";
                }
            };
            res[3] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "Úsečka";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Úsečka" };
                    return res;
                }
            };
            return res;
        }
        
        
    }
    
    private class TwoLinesParameters extends InputParameters{

        @Override
        public String getParametersCaption() {
            return "Osa dvou přímek";
        }

        @Override
        public ParameterItem[] getParameters() {
            ParameterItem[] res = new ParameterItem[5];
            res[0] = new ParameterString() {

                @Override
                public String getText() {
                    return "Název";
                }
            };
            res[1] = new ParameterValue() {

                @Override
                public String getCaption() {
                    return "Název";
                }
            };
            res[2] = new ParameterString() {

                @Override
                public String getText() {
                    return "Přímky k vytvoření osy";
                }
            };
            res[3] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "1. přímka";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Přímka" };
                    return res;
                }
            };
            res[4] = new ParameterObject() {

                @Override
                public String getCaption() {
                    return "2. přímka";
                }

                @Override
                public String[] getObjectNames() {
                    String[] res = { "Přímka" };
                    return res;
                }
            };
            return res;
        }
        
    }
    
}
